home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1996 June / EnigmA AMIGA RUN 08 (1996)(G.R. Edizioni)(IT)[!][issue 1996-06][EARSAN CD VII].iso / earcd / utmisc1 / chktex.lha / chktex / ChkTeX.c < prev    next >
C/C++ Source or Header  |  1996-04-30  |  24KB  |  853 lines

  1. /*
  2.  *  ChkTeX v1.4, finds typographic errors in (La)TeX files.
  3.  *  Copyright (C) 1995-96 Jens T. Berger Thielemann
  4.  *
  5.  *  This program is free software; you can redistribute it and/or modify
  6.  *  it under the terms of the GNU General Public License as published by
  7.  *  the Free Software Foundation; either version 2 of the License, or
  8.  *  (at your option) any later version.
  9.  *
  10.  *  This program is distributed in the hope that it will be useful,
  11.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  *  GNU General Public License for more details.
  14.  *
  15.  *  You should have received a copy of the GNU General Public License
  16.  *  along with this program; if not, write to the Free Software
  17.  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  *
  19.  *  Contact the author at:
  20.  *              Jens Berger
  21.  *              Spektrumvn. 4
  22.  *              N-0666 Oslo
  23.  *              Norway
  24.  *              E-mail: <jensthi@ifi.uio.no>
  25.  *
  26.  *
  27.  */
  28.  
  29. #include "ChkTeX.h"
  30.  
  31. struct Stack
  32.         CharStack       = {0L},
  33.         InputStack      = {0L},
  34.         EnvStack        = {0L};
  35.  
  36. /************************************************************************/
  37.  
  38. const UBYTE BrOrder     [NUMBRACKETS + 1] = "()[]{}";
  39.  
  40. /******************** VARS CHANGED RUNTIME ******************************/
  41.  
  42. ULONG Brackets  [NUMBRACKETS];
  43.  
  44. ULONG   ErrPrint,       /* # errors printed                     */
  45.         WarnPrint,      /* # warnings printed                   */
  46.         UserSupp;       /* # user suppressed warnings           */
  47.  
  48. enum ItState    /* Are we doing italics?                                */
  49.      ItState;
  50.  
  51. /************************************************************************/
  52.  
  53.  
  54. /*
  55.  * Have to do things this way, to ease some checking throughout the
  56.  * program.
  57.  */
  58.  
  59.  
  60. NEWBUF(TmpBuffer,       BUFSIZ);
  61. NEWBUF(ReadBuffer,      BUFSIZ);
  62.  
  63. const STRPTR
  64.         Banner  =
  65. "ChkTeX v1.4 - Copyright 1995-96 Jens T. Berger Thielemann.\n"
  66. #ifdef __MSDOS__
  67. "MS-DOS port by Bj\\o rn Ove Thue\n"
  68. #endif
  69. "\n"
  70. ,
  71.         BigBanner  =
  72. "ChkTeX comes with ABSOLUTELY NO WARRANTY; details on this and\n"
  73. "distribution conditions in the GNU General Public License file.\n"
  74. "Type \"ChkTeX -h\" for help, \"ChkTeX -i\" for distribution info.\n"
  75. "To contact the author, write to:\n"
  76. "Jens Berger, Spektrumvn. 4, N-0666 Oslo, Norway.\n"
  77. "E-mail: <jensthi@ifi.uio.no>\n"
  78. "Press " STDIN_BREAK " to abort stdin input.\n",
  79. Distrib  =
  80. "\n"
  81. "This program is free software; you can redistribute it and/or modify\n"
  82. "it under the terms of the GNU General Public License as published by\n"
  83. "the Free Software Foundation; either version 2 of the License, or\n"
  84. "(at your option) any later version.\n"
  85. "\n"
  86. "This program is distributed in the hope that it will be useful,\n"
  87. "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
  88. "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
  89. "GNU General Public License for more details.\n"
  90. "\n"
  91. "You should have received a copy of the GNU General Public License\n"
  92. "along with this program; if not, write to the Free Software\n"
  93. "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n"
  94. "\n",
  95. OnText  =
  96. "On",
  97. OffText  =
  98. "Off",
  99. HelpText  =
  100. "\n"
  101. "\n"
  102. "                         Usage of ChkTeX v1.4\n"
  103. "                         ~~~~~~~~~~~~~~~~~~~~\n"
  104. "\n"
  105. "                               Template\n"
  106. "                               ~~~~~~~~\n"
  107. "chktex [-hiqr] [-v[0|1|2]] [-l <rcfile>] [-[w|e|m|n] <[0-24]>]\n"
  108. "       [-d <number>] [-o <outputfile>] [-b[0|1]] [-t[0|1]]\n"
  109. "       [-x[0|1]] [-g[0|1]]\n"
  110. "\n"
  111. "----------------------------------------------------------------------\n"
  112. "                       Description of options:\n"
  113. "                       ~~~~~~~~~~~~~~~~~~~~~~~\n"
  114. "Misc. options\n"
  115. "~~~~~~~~~~~~~\n"
  116. "    -h  --help      : This text.\n"
  117. "    -i  --license   : Show distribution information\n"
  118. "    -l  --localrc   : Read local .chktexrc formatted  file.\n"
  119. "    -d  --debug     : Debug information. Give it a number.\n"
  120. "    -r  --reset     : Reset settings to default.\n"
  121. "\n"
  122. "Muting warning messages:\n"
  123. "~~~~~~~~~~~~~~~~~~~~~~~~\n"
  124. "    -w  --warnon    : Makes msg # given a warning and turns it on.\n"
  125. "    -e  --erroron   : Makes msg # given an error and turns it on.\n"
  126. "    -m  --msgon     : Makes msg # given a message and turns it on.\n"
  127. "    -n  --nowarn    : Mutes msg # given.\n"
  128. "\n"
  129. "Output control flags:\n"
  130. "~~~~~~~~~~~~~~~~~~~~~\n"
  131. "    -v  --verbosity : How errors are displayed.\n"
  132. "                      Default 1, 0=Less, 2=Fancy, 3=lacheck.\n"
  133. "    -s  --splitchar : String used to split fields when doing -v0\n"
  134. "    -o  --output    : Redirect error report to a file.\n"
  135. "    -q  --quiet     : Shuts up about version information.\n"
  136. "    -p  --pseudoname: Input file-name when reporting.\n"
  137. "    -f  --format    : Format to use for output\n"
  138. "\n"
  139. "Boolean switches (1 -> enables / 0 -> disables):\n"
  140. "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
  141. "    -b  --backup    : Backup output file.\n"
  142. "    -x  --wipeverb  : Ignore contents of `\\verb' commands.\n"
  143. "    -g  --globalrc  : Read global .chktexrc file.\n"
  144. "    -I  --inputfiles: Execute \\input statements.\n"
  145. "    -H  --headererr : Show errors found in front of \\begin{document}\n"
  146. "\n"
  147. "----------------------------------------------------------------------\n"
  148. "If no LaTeX files are specified on the command line, we will read from\n"
  149. "stdin.   For explanation of warning/error messages, please consult the\n"
  150. "main document ChkTeX.dvi or ChkTeX.ps.\n";
  151.  
  152.  
  153.  
  154. UBYTE EmptyString [] = "";
  155.  
  156.  
  157. /*
  158.  * Options we will set.
  159.  *
  160.  */
  161.  
  162.  
  163. BOOL    
  164.    GlobalRC = TRUE, WipeVerb = TRUE, BackupOut = TRUE,
  165.    Quiet = FALSE, LicenseOnly = FALSE, UsingStdIn = FALSE,
  166.    InputFiles = TRUE, HeadErrOut = TRUE;
  167.  
  168. LONG    DebugLevel = 0;
  169.  
  170. FILE *OutputFile = NULL;
  171.  
  172. STRPTR OutputName = EmptyString, PseudoInName = NULL;
  173.  
  174. UBYTE VerbNormal [] =
  175.   "%k %n in %f line %l: %m\n"
  176.   "%r%s%t\n"
  177.   "%u\n";
  178.  
  179. STRPTR  Delimit = ":";
  180. STRPTR  OutputFormat = VerbNormal;
  181.  
  182.  
  183. STRPTR  PrgName;
  184.  
  185. /*
  186.  * End of config params.
  187.  */
  188.  
  189. /*
  190.  * Duplicates all arguments, and appends an asterix to each of them.
  191.  */
  192.  
  193. static void AddStars(struct WordList *wl)
  194. {
  195.     ULONG Count, CmdLen;
  196.     STRPTR  Data;
  197.  
  198.     for(Count = 0L;
  199.         Count < wl->Stack.Used;
  200.         Count++)
  201.     {
  202.         Data = wl->Stack.Data[Count];
  203.         CmdLen = strlen(Data);
  204.         if(Data[CmdLen - 1] != '*')
  205.         {
  206.             strcpy(TmpBuffer, Data);
  207.             strcat(TmpBuffer, "*");
  208.             InsertWord(TmpBuffer, wl);
  209.         }
  210.     }
  211. }
  212.  
  213.  
  214.  
  215. int main(int argc, char **argv)
  216. {
  217.   int    retval = EXIT_FAILURE, CurArg = 0L;
  218.   ULONG  Count;
  219.   BOOL   StdInUse = FALSE;
  220.   STRPTR NameMatch = EmptyString,
  221.          TmpPtr;
  222.   BOOL   QwkAbbrev;
  223.  
  224. #ifdef __LOCALIZED
  225.   InitStrings();
  226. #endif
  227.  
  228. #ifdef AMIGA
  229.   if(_WBenchMsg)
  230.   {
  231.       if(argv = WB2Argv(_WBenchMsg, W2A_LOWER))
  232.           argc = CountArgv(argv);
  233.       else
  234.           exit(EXIT_FAILURE);
  235.   }
  236. #endif
  237.  
  238.   OutputFile = stdout;
  239.   PrgName = argv[0];
  240.  
  241.   SetupVars();
  242.  
  243.   InsertWord("", &CmdLine);
  244.   InsertWord("", &TeXInputs);
  245.  
  246.   ReadRC(ConfigFile);
  247.  
  248.   if(CmdLine.Stack.Used)
  249.   {
  250.       ParseArgs(CmdLine.Stack.Used, (STRPTR *) CmdLine.Stack.Data);
  251.       CmdLine.Stack.Used = 1L;
  252.   }
  253.   
  254.   if((CurArg = ParseArgs((ULONG) argc, argv)))
  255.   {
  256.       if(CmdLine.Stack.Used)
  257.       {
  258.           ParseArgs(CmdLine.Stack.Used, (STRPTR *) CmdLine.Stack.Data);
  259.           CmdLine.Stack.Used = 1L;
  260.       }
  261.       
  262.       if(!Quiet || LicenseOnly)
  263.           fprintf(stderr, Banner);
  264.  
  265.       if(CurArg == argc)
  266.       UsingStdIn = TRUE;
  267.  
  268.       if((UsingStdIn && !Quiet) || LicenseOnly)
  269.           fprintf(stderr, BigBanner);
  270.  
  271.       if(LicenseOnly)
  272.           fprintf(stderr, Distrib);
  273.       else
  274.       {
  275.           for(Count = 0L;
  276.               Count < Abbrev.Stack.Used;
  277.               Count++)
  278.           {
  279.               QwkAbbrev = TRUE;
  280.  
  281.               TmpPtr = Abbrev.Stack.Data[Count];
  282.               strupr(TmpPtr);
  283.  
  284.               /* Sort out those needing slow searching into a separate array. */
  285.               while(QwkAbbrev && *TmpPtr)
  286.               {
  287.                   QwkAbbrev &= isalpha(*TmpPtr) ||
  288.                         ((*TmpPtr == '.') && !TmpPtr[1]);
  289.                   TmpPtr++;
  290.               }
  291.  
  292.               if(!QwkAbbrev)
  293.               {
  294.                   TmpPtr = Abbrev.Stack.Data[Count];
  295.                   InsertWord(TmpPtr, &SlowAbbrev);
  296.               }
  297.           }
  298.  
  299.           AddStars(&VerbEnvir);
  300.           AddStars(&MathEnvir);
  301.  
  302.           if(DebugLevel)
  303.               ShowIntStatus();
  304.  
  305.           if(OpenOut())
  306.           {
  307.               for(;;)
  308.               {
  309.                   for(Count = 0; Count < NUMBRACKETS; Count++)
  310.                       Brackets[Count] = 0L;
  311.                   
  312.                   ItState  = itOff;
  313.                   AtLetter = MathMode  = VerbMode = FALSE;
  314.                   ErrPrint = WarnPrint = UserSupp = 0L;
  315.                   InHeader = TRUE;
  316.  
  317.                   if(UsingStdIn) {
  318.                       if(StdInUse)
  319.                           break;
  320.                       else {
  321.                           StdInUse = TRUE;
  322.                           PushFile("stdin", stdin, &InputStack);
  323.                       }
  324.                   } else {
  325.                       if((CurArg <= argc) || NameMatch) {
  326.                           ifn(NameMatch = MatchFileName(NULL)) {
  327.                               if(CurArg < argc)
  328.                                   NameMatch = MatchFileName(argv[CurArg++]);
  329.                           }
  330.                           
  331.                           if(!PushFileName(NameMatch, &InputStack))
  332.                               break;
  333.                       }
  334.                   }
  335.                   
  336.                   if(StkTop(&InputStack) && OutputFile)
  337.                   {
  338.                       while(!ferror(OutputFile) && StkTop(&InputStack) &&
  339.                             !ferror(CurStkFile(&InputStack)) &&
  340.                             FGetsStk(ReadBuffer, BUFSIZ-1, &InputStack))
  341.                       {
  342.                           
  343.                           /* Make all spaces ordinary spaces */
  344.                           
  345.                           strrep(ReadBuffer, '\n', ' ');
  346.                           strrep(ReadBuffer, '\r', ' ');
  347.                           strrep(ReadBuffer, '\t', ' ');
  348.                           
  349.                           strcat(ReadBuffer, " ");
  350.                           FindErr(ReadBuffer, CurStkLine(&InputStack));
  351.                       }
  352.                       
  353.                       PrintStatus(CurStkLine(&InputStack));
  354.                       retval = EXIT_SUCCESS;
  355.                   }
  356.               }
  357.           }
  358.       }
  359.   }
  360.   exit(retval);
  361. }
  362.  
  363. /*
  364.  * Opens the output file handle & possibly renames
  365.  */
  366.  
  367. BOOL OpenOut(void)
  368. {
  369.     BOOL Success = TRUE;
  370.     
  371.     if(*OutputName)
  372.     {
  373.         if(BackupOut && fexists(OutputName))
  374.         {
  375.             strncpy(TmpBuffer, OutputName, BUFSIZ - 4);
  376.             strcat(TmpBuffer, ".bak");
  377.             
  378.             if(fexists(TmpBuffer))
  379.                 remove(TmpBuffer);
  380.  
  381.             if(!rename(OutputName, TmpBuffer))
  382.                 PrintPrgErr(pmRename,
  383.                             OutputName, TmpBuffer);
  384.             else
  385.             {
  386.                 PrintPrgErr(pmRenameErr,
  387.                             OutputName, TmpBuffer);
  388.                 Success = FALSE;
  389.             }
  390.             
  391.         }
  392.         
  393.         if(Success)
  394.         {
  395.             ifn(OutputFile = fopen(OutputName, "w"))
  396.             {
  397.                 PrintPrgErr(pmOutOpen);
  398.                 Success = FALSE;
  399.             }
  400.         }
  401.     }
  402.     else
  403.         OutputFile = stdout;
  404.     
  405.     return(Success);
  406. }
  407.  
  408.  
  409. #define BOOLDISP(var)           ((var)? OnText : OffText)
  410. #define SHOWSTAT(text, arg)     fprintf(stderr, "\t" text ": %s\n", arg)
  411. #define BOOLSTAT(name, var)     SHOWSTAT(name, BOOLDISP(var))
  412. #define SHOWWORD(text, list)    fprintf(stderr, "\n" text "\n"); \
  413.                                                         for(Cnt = 0L; \
  414.                                                                 Cnt < list.Stack.Used; \
  415.                                                                 Cnt++) \
  416.                                                                 fprintf(stderr, "\t%s\n", (STRPTR) list.Stack.Data[Cnt])
  417. #define SHOWSTR(text, arg)      fprintf(stderr, "%s:\n\t%s\n", text, arg);
  418.  
  419.  
  420. /*
  421.  * Prints some of the internal flags; mainly for debugging purposes
  422.  */
  423.  
  424. void ShowIntStatus(void)
  425. {
  426.     ULONG       Cnt;
  427.     STRPTR      String;
  428.  
  429.     fprintf(stderr, "There are %d warnings/error messages available:\n", 
  430.             emMaxFault - emMinFault - 1);
  431.  
  432.     for(Cnt = emMinFault + 1; Cnt < emMaxFault; Cnt++)
  433.     {
  434.         switch(LaTeXMsgs[Cnt].Type)
  435.         {
  436.         case etWarn:    String = "Warning"; break;
  437.         case etErr:     String = "Error"; break;
  438.         case etMsg:     String = "Message"; break;
  439.     default:        String = ""; break;
  440.         }
  441.  
  442.         fprintf(stderr, "Number: %2ld, Type: %s, Status: %s\n"
  443.                 "\tText: %s\n\n",
  444.                 Cnt, String,
  445.                 BOOLDISP(LaTeXMsgs[Cnt].InUse),
  446.                 LaTeXMsgs[Cnt].Message);
  447.     }
  448.  
  449.     SHOWWORD("Abbreviations searched for:", Abbrev);
  450.     SHOWWORD("`Silent' commands:", Silent);
  451.     SHOWWORD("`Linked' commands:", Linker);
  452.     SHOWWORD("Accenting commands needing `\\i' and `\\j':", IJAccent);
  453.     SHOWWORD("Italizing commands:", Italic);
  454.     SHOWWORD("User patterns:", UserWarn);
  455.     SHOWWORD("Slow-mode abbrevs:", SlowAbbrev);
  456.     SHOWWORD("Wipe arguments of:", WipeArg);
  457.     SHOWWORD("Ignore contents of environments:", VerbEnvir);
  458.     SHOWWORD("Treat contents of environments as math:", MathEnvir);
  459.     SHOWWORD("Hyphen dash len:", HyphDash);
  460.     SHOWWORD("Numeric dash len:", NumDash);
  461.     SHOWWORD("Interword dash len:", WordDash);
  462.     SHOWWORD("\\cdots items:", CenterDots);
  463.     SHOWWORD("\\ldots items:", LowDots);
  464.     SHOWWORD("Math roman:", MathRoman);
  465.     SHOWWORD("TeX input paths:", TeXInputs);
  466.     SHOWWORD("Available output formats", OutFormat);
  467.     SHOWWORD("Post-link commands", PostLink);
  468.  
  469.     SHOWSTR("Outputformat", OutputFormat);
  470.     SHOWSTR("VerbClear", VerbClear);
  471.  
  472.     fprintf(stderr, "Current flags include:\n");
  473.     
  474.     BOOLSTAT("Read global resource", GlobalRC);
  475.     BOOLSTAT("Wipe verbose commands", WipeVerb);
  476.     BOOLSTAT("Backup outfile", BackupOut);
  477.     BOOLSTAT("Quiet mode", Quiet);
  478.     BOOLSTAT("Show license", LicenseOnly);
  479.     BOOLSTAT("Use stdin", UsingStdIn);
  480.     BOOLSTAT("\\input files", InputFiles);
  481.     BOOLSTAT("Output header errors", HeadErrOut);
  482. }
  483.  
  484. /*
  485.  * Resets all stacks.
  486.  *
  487.  */
  488.  
  489.  
  490. static void ResetStacks(void)
  491. {
  492.     Silent.Stack.Used = 0;          
  493.     Abbrev.Stack.Used = 0;          
  494.     SlowAbbrev.Stack.Used = 0;      
  495.     Linker.Stack.Used = 0;          
  496.     IJAccent.Stack.Used = 0;        
  497.     Italic.Stack.Used = 0;          
  498.     ItalCmd.Stack.Used = 0;         
  499.     UserWarn.Stack.Used = 0;        
  500.     CmdLine.Stack.Used = 0;         
  501.     PostLink.Stack.Used = 0;        
  502.     WipeArg.Stack.Used = 0;         
  503.     VerbEnvir.Stack.Used = 0;       
  504.     MathEnvir.Stack.Used = 0;       
  505.     MathRoman.Stack.Used = 0;       
  506.     TeXInputs.Stack.Used = 0;       
  507.     HyphDash.Stack.Used = 0;        
  508.     NumDash.Stack.Used = 0;         
  509.     WordDash.Stack.Used = 0;        
  510.     CenterDots.Stack.Used = 0;      
  511.     LowDots.Stack.Used = 0;         
  512.     OutFormat.Stack.Used = 0;       
  513. }
  514.  
  515.  
  516.  
  517. /*
  518.  * Resets all flags (not wordlists) to their default values. Sets
  519.  * Outputfile to stdout.
  520.  *
  521.  */
  522.  
  523. static void ResetSettings(void)
  524. {
  525.     GlobalRC = TRUE;
  526.     WipeVerb = TRUE;
  527.     BackupOut = TRUE;
  528.     Quiet = FALSE;
  529.     LicenseOnly = FALSE;
  530.     UsingStdIn = FALSE;
  531.     DebugLevel = 0;
  532.     InputFiles = TRUE;
  533.     HeadErrOut = TRUE;
  534.     OutputFormat = VerbNormal;
  535.     
  536.     if(OutputFile != stdout)
  537.     {
  538.         fclose(OutputFile);
  539.         OutputFile = stdout;
  540.     }
  541.     
  542.     OutputName = EmptyString;
  543. }
  544.  
  545. /*
  546.  * Reads a numerical argument from the argument. Supports concatenation
  547.  * of arguments (main purpose)
  548.  */
  549.  
  550. int ParseNumArg(LONG *Dest,             /* Where to put the value */
  551.                 LONG Default,           /* Value to put in if no in argue */
  552.                 STRPTR *Argument)       /* optarg or similar */
  553. {
  554.     if(Argument && *Argument && isdigit(**Argument))
  555.         *Dest = strtol(*Argument, Argument, 10);
  556.     else
  557.         *Dest = Default;
  558.     
  559.     return(ShiftArg(Argument));
  560. }
  561.  
  562. /*
  563.  * Same as above; however, will toggle the boolean if user doesn't
  564.  * supply value
  565.  */
  566.  
  567. int ParseBoolArg(BOOL *Dest,            /* Boolean value */
  568.                  STRPTR *Argument)      /* optarg or similar */
  569. {
  570.     LONG        D = *Dest? 1L : 0L;
  571.     int         Retval;
  572.     
  573.     Retval = ParseNumArg(&D, *Dest? 0L : 1L, Argument);
  574.     
  575.     *Dest = D ? TRUE : FALSE;
  576.     
  577.     return(Retval);
  578. }
  579.  
  580. /*
  581.  * Returns the first character in the string passed, updates the
  582.  * string pointer, if the string is non-empty.
  583.  *
  584.  * 0 if the string is empty.
  585.  */
  586.  
  587. int ShiftArg(STRPTR *Argument)                  /* optarg or similar */
  588. {
  589.     if(Argument && *Argument && **Argument)
  590.         return(*((STRPTR) (*Argument)++));
  591.     else
  592.         return 0;
  593. }
  594.  
  595. /*
  596.  * Parses an argv similar array.
  597.  */
  598.  
  599.  
  600. int ParseArgs(int argc, char **argv)
  601. {
  602.     /* Needed for option parsing. */
  603.  
  604. static const
  605.   struct option long_options[] =
  606.   {
  607.     {"help",            no_argument,            0L,     'h'},
  608.     {"localrc",         required_argument,      0L,     'l'},
  609.     {"output",          required_argument,      0L,     'o'},
  610.     {"warnon",          required_argument,      0L,     'w'},
  611.     {"erroron",         required_argument,      0L,     'e'},
  612.     {"msgon",           required_argument,      0L,     'm'},
  613.     {"nowarn",          required_argument,      0L,     'n'},
  614.     {"verbosity",       optional_argument,      0L,     'v'},
  615.     {"debug",           required_argument,      0L,     'd'},
  616.     {"reset",           no_argument,            0L,     'r'},
  617.     {"quiet",           no_argument,            0L,     'q'},
  618.     {"license",         no_argument,            0L,     'i'},
  619.     {"splitchar",       required_argument,      0L,     's'},
  620.     {"format",          required_argument,      0L,     'f'},
  621.     {"pseudoname",      required_argument,      0L,     'p'},
  622.  
  623.     {"inputfiles",      optional_argument,      0L,     'I'},
  624.     {"backup",          optional_argument,      0L,     'b'},
  625.     {"globalrc",        optional_argument,      0L,     'g'},
  626.     {"wipeverb",        optional_argument,      0L,     'x'},
  627.     {"tictoc",          optional_argument,      0L,     't'},
  628.     {"headererr",       optional_argument,      0L,     'H'},
  629.     {0L,                0L,                     0L,      0L}
  630.   };
  631.  
  632.   int   option_index = 0L, c, nextc, ErrType = 0;
  633.  
  634.   int   Retval = FALSE, InUse;
  635.   BOOL  Success = FALSE, Foo;
  636.   LONG  Err, Verb = 1;
  637.  
  638.   enum
  639.   {
  640.       aeNoErr = 0,
  641.       aeArg,              /* missing/bad required argument */
  642.       aeOpt,              /* unknown option returned */
  643.       aeHelp,             /* just a call for help */
  644.       aeMem               /* no memory */
  645.   } ArgErr = aeNoErr;
  646.  
  647.   optind = 0;
  648.  
  649.   while(!ArgErr &&
  650.         ((c = getopt_long((int) argc, argv,
  651.                           "b::d:e:f:g::hH::I::il:m:n:o:p:qrs:t::v::w:x::",
  652.                           long_options,
  653.                           &option_index)) != EOF))
  654.   {
  655.       while(c) {
  656.           nextc = 0;
  657.           switch(c)
  658.           {
  659.           case 's':
  660.               ifn(Delimit = strdup(optarg)) {
  661.                   PrintPrgErr(pmStrDupErr);
  662.                   ArgErr = aeMem;
  663.               }
  664.  
  665.               break;
  666.           case 'p':
  667.               ifn(PseudoInName = strdup(optarg)) {
  668.                   PrintPrgErr(pmStrDupErr);
  669.                   ArgErr = aeMem;
  670.               }
  671.  
  672.               break;
  673.       
  674.           case 'd':
  675.               nextc = ParseNumArg(&DebugLevel, 0, &optarg);
  676.  
  677.               break;
  678.           case 'i':
  679.               LicenseOnly = TRUE;
  680.               
  681.               nextc = ShiftArg(&optarg);
  682.               break;
  683.           case 'q':
  684.               Quiet = TRUE;
  685.               
  686.               nextc = ShiftArg(&optarg);
  687.               break;
  688.               
  689.               /* Variation of Duff's device... */
  690.               do {
  691.               case 'w':
  692.                   ErrType = etWarn;
  693.                   InUse = TRUE;
  694.                   break;
  695.               case 'e':
  696.                   ErrType = etErr;
  697.                   InUse = TRUE;
  698.                   break;
  699.               case 'm':
  700.                   ErrType       = etMsg;
  701.                   InUse = TRUE;
  702.                   break;
  703.               case 'n':
  704.                   InUse = FALSE;
  705.               } while(FALSE);
  706.               
  707.               nextc = ParseNumArg(&Err, -1, &optarg);
  708.  
  709.               if(betw(emMinFault, Err, emMaxFault)) {
  710.                   LaTeXMsgs[Err].Type   = ErrType;
  711.                   LaTeXMsgs[Err].InUse = InUse;
  712.               } else {
  713.                   ArgErr = aeOpt;
  714.                   PrintPrgErr(pmWarnNumErr);
  715.               }
  716.               break;
  717.  
  718.           case 'g':
  719.               nextc = ParseBoolArg(&GlobalRC, &optarg);
  720.               if(!GlobalRC) {
  721.           ResetStacks();
  722.               }
  723.               break;
  724.               
  725.           case 'r':
  726.               ResetSettings();
  727.               nextc = ShiftArg(&optarg);
  728.               break;
  729.               
  730.           case 'l':
  731.               if(optarg)
  732.                   ReadRC(optarg);
  733.               break;
  734.               
  735.           case 'f':
  736.               ifn(OutputFormat = strdup(optarg))
  737.               {
  738.                   PrintPrgErr(pmStrDupErr);
  739.                   ArgErr = aeMem;
  740.               }
  741.               break;
  742.  
  743.           case 'v':
  744.               nextc = ParseNumArg(&Verb, 2, &optarg);
  745.  
  746.           if(Verb < (LONG) OutFormat.Stack.Used)
  747.           OutputFormat = strdup(OutFormat.Stack.Data[Verb]);
  748.           else
  749.           {
  750.                   PrintPrgErr(pmVerbLevErr);
  751.                   ArgErr = aeArg;
  752.           }
  753.               break;
  754.  
  755.           case 'o':
  756.               if(optarg)
  757.               {
  758.                   if(*OutputName)
  759.                   {
  760.                       PrintPrgErr(pmOutTwice);
  761.                       ArgErr = aeOpt;
  762.                   }
  763.                   else
  764.                   {
  765.                       ifn(OutputName = strdup(optarg))
  766.                       {
  767.                           PrintPrgErr(pmStrDupErr);
  768.                           ArgErr = aeMem;
  769.                       }
  770.                   }
  771.               }
  772.  
  773.               break;
  774.  
  775.           case 't':
  776.               nextc = ParseBoolArg(&Foo, &optarg);
  777.               break;
  778.           case 'x':
  779.               nextc = ParseBoolArg(&WipeVerb, &optarg);
  780.               break;
  781.           case 'b':
  782.               nextc = ParseBoolArg(&BackupOut, &optarg);
  783.               break;
  784.           case 'I':
  785.               nextc = ParseBoolArg(&InputFiles, &optarg);
  786.               break;
  787.           case 'H':
  788.               nextc = ParseBoolArg(&HeadErrOut, &optarg);
  789.               break;
  790.           case 'h':
  791.           case '?':
  792.           default: 
  793.               ArgErr = aeHelp;
  794.               break;
  795.           }
  796.           c = nextc;
  797.       }
  798.   }
  799.  
  800.   if((argc > optind) && !strcmp(argv[optind], "?"))
  801.       ArgErr = aeHelp;
  802.  
  803.   if(ArgErr)
  804.   {
  805.       fputs(Banner, stderr);
  806.       fputs(BigBanner, stderr);
  807.       fputs(HelpText, stderr);
  808.       Success = FALSE;
  809.   }
  810.   else
  811.       Success = TRUE;
  812.  
  813.   if(Success)
  814.       Retval = optind;
  815.  
  816.   return(Retval);
  817. }
  818.  
  819. /*
  820.  * Outputs a program error.
  821.  */
  822.  
  823.  
  824. void PrintPrgErr(enum PrgErrNum Error, ...)
  825. {
  826.     STRPTR      Type;
  827.     va_list     MsgArgs;
  828.     
  829.     if(betw(pmMinFault, Error, pmMaxFault)) {
  830.         switch(PrgMsgs[Error].Type) {
  831.         case etWarn:
  832.             Type = "WARNING";
  833.             break;
  834.         case etMsg:
  835.             Type = "NOTE";
  836.             break;
  837.     default:
  838.         case etErr:
  839.             Type = "ERROR";
  840.             break;
  841.         }
  842.         fprintf(stderr, "%s: %s -- ", PrgName, Type);
  843.         
  844.         va_start(MsgArgs, Error);
  845.         vfprintf(stderr, PrgMsgs[Error].Message, MsgArgs);
  846.         va_end(MsgArgs);
  847.         fputc('\n', stderr);
  848.     }
  849. }
  850.  
  851.  
  852.  
  853.